package com.uuah.server.util.store;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.exception.util.file.DeleteException;
import com.uuah.queue.QueuePersistenceStrategy;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.context.IUuahContextAware;
import com.uuah.utils.FileUtils;
import com.uuah.utils.UUID;

public class FilePersistenceStrategy implements QueuePersistenceStrategy,
		IUuahContextAware {
	private static final Logger logger = LoggerFactory
			.getLogger(FilePersistenceStrategy.class);

	public static final String DEFAULT_QUEUE_STORE = "queuestore";

	public static final String EXTENSION = ".msg";

	private File store;

	protected IUuahContext uuahContext;

	public FilePersistenceStrategy() {
		super();
	}

	public void setUuahContext(IUuahContext context) {
		this.uuahContext = context;
	}

	public Object store(String queue, Object obj) throws IOException {
		String id = UUID.getRandomBasedUUIDString();
		File file = FileUtils.newFile(store, queue + File.separator + id
				+ EXTENSION);
		file.getParentFile().mkdirs();
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
				file));
		oos.writeObject(obj);
		oos.close();
		return id;
	}

	public void remove(String queue, Object id) throws IOException {
		File file = FileUtils.newFile(store, queue + File.separator + id
				+ EXTENSION);
		if (file.exists()) {
			if (!file.delete()) {
				throw new DeleteException(file);
			}
		} else {
			throw new FileNotFoundException(file.toString());
		}
	}

	public Object load(String queue, Object id) throws IOException {
		File file = FileUtils.newFile(store, queue + File.separator + id
				+ EXTENSION);
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream(file));
			Object obj = ois.readObject();
			return obj;
		} catch (ClassNotFoundException e) {
			throw (IOException) new IOException(
					"Error loading persistent object").initCause(e);
		} finally {
			if (ois != null) {
				ois.close();
			}
		}
	}

	public List restore() throws IOException {
		List msgs = new ArrayList();
		if (store == null) {
			logger
					.warn("No store has be set on the File Persistence Strategy. Not restoring at this time");
			return msgs;
		}
		try {
			restoreFiles(store, msgs);
			if (logger.isDebugEnabled()) {
				logger.debug("Restore retrieved " + msgs.size() + " objects");
			}
			return msgs;
		} catch (ClassNotFoundException e) {
			throw (IOException) new IOException("Could not restore")
					.initCause(e);
		}
	}

	protected void restoreFiles(File dir, List msgs) throws IOException,
			ClassNotFoundException {
		File[] files = dir.listFiles();
		if (files == null) {
			return;
		}

		for (int i = 0; i < files.length; i++) {
			if (files[i].isDirectory()) {
				restoreFiles(files[i], msgs);
			} else if (files[i].getName().endsWith(EXTENSION)) {
				String id = files[i].getCanonicalPath();
				id = id.substring(store.getCanonicalPath().length() + 1, id
						.length()
						- EXTENSION.length());
				String queue = id.substring(0, id.indexOf(File.separator));
				id = id.substring(queue.length() + 1);
				msgs.add(new HolderImpl(queue, id));
			}
		}
	}

	public void open() throws IOException {
		String path = uuahContext.getConfiguration().getWorkingDirectory()
				+ File.separator + DEFAULT_QUEUE_STORE;
		store = FileUtils.newFile(path).getCanonicalFile();
		store.mkdirs();
	}

	public void close() throws IOException {
		// Nothing to do
	}

	protected static class HolderImpl implements Holder {
		private String queue;
		private Object id;

		public HolderImpl(String queue, Object id) {
			this.queue = queue;
			this.id = id;
		}

		public Object getId() {
			return id;
		}

		public String getQueue() {
			return queue;
		}
	}

	public boolean isTransient() {
		return false;
	}
}
